package com.andova.capture

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.media.AudioManager
import android.media.MediaPlayer
import android.os.Build
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.Surface
import android.view.View
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.core.view.children
import com.andova.capture.CaptureButton.BUTTON_STATE_ONLY_CAPTURE
import com.andova.capture.CaptureButton.BUTTON_STATE_ONLY_RECORDER
import com.andova.ext.e
import com.andova.glcamera.FRAME_ACTION_NORMAL
import com.andova.glcamera.FRAME_ACTION_PREVIEW
import com.andova.glcamera.GlCamera2View
import com.andova.glcamera.R
import com.andova.glcamera.device.RecorderBuilder
import java.io.File
import java.io.IOException

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class GlCaptureView(context: Context, attrs: AttributeSet?) : GlCamera2View(context, attrs), CaptureListener, View.OnClickListener {
    private var mReturnBottomMarginStart = 0
    private var mReturnBottomMarginTop = 0
    private var mReturnBottomMarginEnd = 0
    private var mReturnBottomMarginBottom = 0
    private var mCaptureBottomMarginBottom = 0
    private var mTypeBottomMarginBottom = 0

    private var isPortrait = false
    private val metrics = DisplayMetrics()

    private var mBtnCapture: CaptureButton? = null
    private var mBtnReturn: ReturnButton? = null
    private var mBtnCancel: TypeButton? = null
    private var mBtnConfirm: TypeButton? = null
    private var mTvTip: TextView? = null
    private var outPicture: File? = null
    private var recordBuilder: RecorderBuilder? = null
    private var mMediaPlayer: MediaPlayer? = null

    private var mPath: String = ""
    private var actionConfirm: (path: String) -> Unit = {}
    private var actionFinish: () -> Unit = {}

    init {
        isPortrait = resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT // 竖屏标志
        (context as Activity).windowManager.defaultDisplay.getMetrics(metrics)

        val a = context.obtainStyledAttributes(attrs, R.styleable.GlCaptureView)
        mReturnBottomMarginStart = a.getDimensionPixelOffset(R.styleable.GlCaptureView_capture_returnButtonMarginStart, (16 * metrics.density).toInt())
        mReturnBottomMarginTop = a.getDimensionPixelOffset(R.styleable.GlCaptureView_capture_returnButtonMarginTop, (16 * metrics.density).toInt())
        mReturnBottomMarginEnd = a.getDimensionPixelOffset(R.styleable.GlCaptureView_capture_returnButtonMarginEnd, 0)
        mReturnBottomMarginBottom = a.getDimensionPixelOffset(R.styleable.GlCaptureView_capture_returnButtonMarginBottom, 0)
        mCaptureBottomMarginBottom = a.getDimensionPixelOffset(R.styleable.GlCaptureView_capture_captureButtonMarginBottom, (16 * metrics.density).toInt())
        mTypeBottomMarginBottom = a.getDimensionPixelOffset(R.styleable.GlCaptureView_capture_typeButtonMarginBottom, (32 * metrics.density).toInt())
        a.recycle()
    }

    override fun onAttached() {
        LayoutInflater.from(context).inflate(R.layout.layout_capture_view, this).apply {
            mBtnCapture = findViewById(R.id.btn_capture)
            mBtnReturn = findViewById(R.id.btn_return)
            mBtnCancel = findViewById(R.id.btn_cancel)
            mBtnConfirm = findViewById(R.id.btn_confirm)
            mTvTip = findViewById(R.id.tv_tip)
        }

        mTvTip?.text = getCaptureTip()
        mBtnCapture?.setCaptureListener(this)
        mBtnCancel?.setOnClickListener(this)
        mBtnConfirm?.setOnClickListener(this)
        mBtnReturn?.setOnClickListener(this)
    }

    override fun onDetached() {
        mMediaPlayer?.stop()
        mMediaPlayer?.release()
        mMediaPlayer = null
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        for (child in children) {
            when (child) {
                is ReturnButton -> {
                    val l = left + mReturnBottomMarginStart
                    val t = top + mReturnBottomMarginTop
                    val r = l + child.width
                    val b = t + child.height
                    child.layout(l, t, r, b)
                }
                is TypeButton -> when (child.buttonType) {
                    TypeButton.TYPE_CANCEL -> {
                        val l = left + metrics.widthPixels / 4 - child.width / 2
                        val t = bottom - child.height - mTypeBottomMarginBottom
                        val r = l + child.width
                        val b = t + child.height
                        child.layout(l, t, r, b)
                    }
                    TypeButton.TYPE_CONFIRM -> {
                        val l = right - metrics.widthPixels / 4 - child.width / 2
                        val t = bottom - child.height - mTypeBottomMarginBottom
                        val r = l + child.width
                        val b = t + child.height
                        child.layout(l, t, r, b)
                    }
                }
                is CaptureButton -> {
                    val l = left + metrics.widthPixels / 2 - child.width / 2
                    val t = bottom - child.height - mCaptureBottomMarginBottom
                    val r = l + child.width
                    val b = t + child.height
                    child.layout(l, t, r, b)
                }
                is TextView -> {
                    val l = left + metrics.widthPixels / 2 - child.width / 2
                    val t = bottom - child.height * 2 - (mBtnCapture?.height ?: 0)
                    val r = l + child.width
                    val b = t + child.height
                    child.layout(l, t, r, b)
                }
            }
        }
    }

    override fun onTakePictures() {
        action(FRAME_ACTION_NORMAL)
        Thread.sleep(100L)
        mTvTip?.visibility = GONE
        mBtnCapture?.setButtonCaptureEnabled(false)
        getCamera()?.takePicture(outPicture) {
            if (it == null) return@takePicture resetCaptureView()
            mPath = it.path
            action(FRAME_ACTION_PREVIEW)
            startTypeButtonAnimator()
        }
    }

    override fun onRecordShort(time: Long) {
        getCamera()?.stopRecord()
        deleteFile(recordBuilder?.out)
        resetCaptureView()
    }

    override fun onRecordStart() {
        mTvTip?.visibility = GONE
        getCamera()?.startRecord(recordBuilder ?: return)
    }

    override fun onRecordEnd(time: Long) {
        getCamera()?.stopRecord()
        startTypeButtonAnimator()
        val path = recordBuilder?.out ?: return
        try {
            playVideo(path)
        } catch (e: IllegalStateException) {
            e(e.message ?: "IllegalStateException for playVideo")
        }
        mPath = path
    }

    override fun onRecordZoom(zoom: Float) {
    }

    override fun onRecordError() {
        getCamera()?.stopRecord()
        deleteFile(recordBuilder?.out)
        resetCaptureView()
    }

    override fun onClick(view: View?) {
        when (view?.id) {
            R.id.btn_cancel -> resetCaptureView()
            R.id.btn_confirm -> actionConfirm(mPath)
            R.id.btn_return -> actionFinish()
        }
    }

    private fun deleteFile(path: String?): Boolean {
        path ?: return false
        val file = File(path)
        if (file.exists()) return file.delete()
        return false
    }

    private fun getCaptureTip(): String = when (mBtnCapture?.buttonFeatures) {
        BUTTON_STATE_ONLY_CAPTURE -> context.getString(R.string.picture_photo_pictures)
        BUTTON_STATE_ONLY_RECORDER -> context.getString(R.string.picture_photo_recording)
        else -> context.getString(R.string.picture_photo_camera)
    }

    /**
     * 拍照录制结果后的动画
     */
    @SuppressLint("ObjectAnimatorBinding")
    private fun startTypeButtonAnimator() {
        mBtnCapture?.visibility = GONE
        mBtnCancel?.visibility = VISIBLE
        mBtnConfirm?.visibility = VISIBLE
        mBtnCancel?.isClickable = false
        mBtnConfirm?.isClickable = false
        val animcancel = ObjectAnimator.ofFloat(mBtnCancel, "translationX", (metrics.widthPixels / 4).toFloat(), 0f)
        val animconfirm = ObjectAnimator.ofFloat(mBtnConfirm, "translationX", (-metrics.widthPixels / 4).toFloat(), 0f)
        val set = AnimatorSet()
        set.playTogether(animcancel, animconfirm)
        set.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                mBtnCancel?.isClickable = true
                mBtnConfirm?.isClickable = true
            }
        })
        set.duration = 500
        set.start()
    }

    private fun resetCaptureView() {
        mBtnCapture?.resetState()
        mBtnCapture?.setButtonCaptureEnabled(true)
        mBtnCancel?.visibility = GONE
        mBtnConfirm?.visibility = GONE
        mBtnCapture?.visibility = VISIBLE
        mTvTip?.text = getCaptureTip()
        mTvTip?.visibility = VISIBLE

        action(FRAME_ACTION_PREVIEW).resume()
        stopVideo()
        getCamera()?.resetCaptureSession()
    }

    @Throws(IOException::class)
    private fun playVideo(url: String) {
        mMediaPlayer = MediaPlayer().apply {
            setDataSource(url)
            setSurface(Surface(getCamera()?.getSurfaceTexture()))
            setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT)
            setAudioStreamType(AudioManager.STREAM_MUSIC)
            setOnVideoSizeChangedListener { mp, width, height ->
            }
            setOnPreparedListener { start() }
            isLooping = true
            prepareAsync()
        }
    }

    private fun stopVideo() {
        if (mMediaPlayer?.isPlaying != true) return
        mMediaPlayer?.apply {
            stop()
            reset()
            release()
        }
        mMediaPlayer = null
    }

    fun capture(picture: File, recorder: RecorderBuilder): GlCaptureView {
        this.outPicture = picture
        this.recordBuilder = recorder
        action(FRAME_ACTION_PREVIEW)
        return this
    }

    fun confirm(action: (path: String) -> Unit): GlCaptureView {
        actionConfirm = action
        return this
    }

    fun finish(action: () -> Unit): GlCaptureView {
        actionFinish = action
        return this
    }
}